package dht

import (
	"crypto/rand"
	"encoding/base64"
	"errors"
	"math/big"
	"sort"
)

type ID [20]byte

func GenerateID() (id ID, err error) {
	buf := make([]byte, 20)
	_, err = rand.Reader.Read(buf)
	if err != nil {
		return id, err
	}
	for i, b := range buf {
		id[i] = b
	}
	return id, nil
}

func FromID(buf []byte) (ID, error) {
	var id ID
	if len(buf) != 20 {
		return id, errors.New("From ID Error")
	}
	for i := range id {
		id[i] = buf[i]
	}
	return id, nil
}

func (id ID) String() string {
	return base64.URLEncoding.EncodeToString(id[:])
}

func (id ID) Xor(other ID) ID {
	var ret ID
	for i, b := range id {
		ret[i] = other[i] ^ b
	}
	return ret
}

func (id ID) BitLen() int {
	var num big.Int
	num.SetBytes(id[:])
	return num.BitLen()
}

func (id ID) Bytes() []byte {
	return id[:]
}

type IDList []ID

type IDSort struct {
	Target ID
	List   IDList
}

func (list IDList) Sort(target ID) IDList {
	is := &IDSort{
		Target: target,
		List:   list,
	}
	sort.Sort(is)
	return is.List
}

func (list IDList) Append(other ID) IDList {
	for _, v := range list {
		if v == other {
			return list
		}
	}
	return append(list, other)
}

func (is *IDSort) Len() int {
	return len(is.List)
}

func (is *IDSort) Less(i, j int) bool {
	var a big.Int
	var b big.Int
	a.SetBytes(is.List[i].Xor(is.Target).Bytes())
	b.SetBytes(is.List[j].Xor(is.Target).Bytes())
	return a.Cmp(&b) == -1
}

func (is *IDSort) Swap(i, j int) {
	is.List[i], is.List[j] = is.List[j], is.List[i]
}
